<!-- 我的树形下拉框 -->
<template>
<div class="my-select-tree-wp">
  <div v-popover:popover ref="toggleBtn">
    <el-cascader ref="cascader"
      class="the-cascader"
      v-model="cascaderVal"
      :options="options"
      :props="cascaderProps"
      :size="size"
      :disabled="disabled"
      :clearable="clearable"
      :filterable="filterable"
      :collapseTags="collapseTags"
      :show-all-levels="showAllLevels"
      :separator="separator"
      popper-class="my-select-tree-hide-popper"
      @visible-change="visibleChange"
      @keyup.native="keyup($event)">
    </el-cascader>
  </div>
    
  <el-popover v-if="!disabled"
    ref="popover"
    v-model="popoverVisible"
    :width="popperWidth"
    popper-class="my-select-tree-popper"
    placement="bottom-start"
    trigger="click"
    @show="setDefaultState">
    <el-scrollbar class="scroll-box">
      <!-- 多选 -->
      <template v-if="multiple">
        <el-tree class="tree" ref="tree"
          :data="options"
          :props="treeProps"
          :node-key="valueKey"
          show-checkbox
          :default-checked-keys="checkedKeys"
          :default-expanded-keys="expandedKeys"
          :expand-on-click-node="false"
          :check-strictly="true"
          :filter-node-method="filterNodeMethod"
          @check-change="checkChange"></el-tree>
      </template>
      <!-- 单选 -->
      <template v-else>
        <el-tree class="tree" ref="tree"
          :data="options"
          :props="treeProps"
          :node-key="valueKey"
          :current-node-key="value"
          :default-expanded-keys="expandedKeys"
          :expand-on-click-node="false"
          :check-on-click-node="true"
          :highlight-current="true"
          :filter-node-method="filterNodeMethod"
          @current-change="currentChange"></el-tree>
      </template>
    </el-scrollbar>
  </el-popover>
</div>
</template>
<script>

// 给节点绑定resize事件
var onelresize = function (el, handler) {
  if (!(el instanceof HTMLElement)) {
    throw new TypeError("参数1不是HTMLElement实例对象。")
  }
  if (/^(area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr|script|style|textarea|title)$/i.test(el.tagName)) {
    throw new TypeError('不支持的标签类型。更改标签或将其包装在支持的标记中（例如，DIV）。')
  }
  if (typeof handler !== 'function') { 
    throw new TypeError("参数2不是类型不是方法。") 
  }

  var lastWidth = el.offsetWidth || 1
  var lastHeight = el.offsetHeight || 1
  var maxWidth = 10000 * (lastWidth)
  var maxHeight = 10000 * (lastHeight)

  var expand = document.createElement('div')
  expand.style.cssText = 'position:absolute;top:0;bottom:0;left:0;right:0;z-index=-10000;overflow:hidden;visibility:hidden;'
  var shrink = expand.cloneNode(false)

  var expandChild = document.createElement('div')
  expandChild.style.cssText = 'transition:0s;animation:none;'
  var shrinkChild = expandChild.cloneNode(false)

  expandChild.style.width = maxWidth + 'px'
  expandChild.style.height = maxHeight + 'px'
  shrinkChild.style.width = '250%'
  shrinkChild.style.height = '250%'

  expand.appendChild(expandChild)
  shrink.appendChild(shrinkChild)
  el.appendChild(expand)
  el.appendChild(shrink)

  if (expand.offsetParent !== el) {
    el.style.position = 'relative'
  }

  expand.scrollTop = shrink.scrollTop = maxHeight
  expand.scrollLeft = shrink.scrollLeft = maxWidth

  var newWidth = 0
  var newHeight = 0
  function onResize () {
    if (newWidth !== lastWidth || newHeight !== lastHeight) {
        lastWidth = newWidth
        lastHeight = newHeight
        handler()
    }
  }

  function onScroll () {
    newWidth = el.offsetWidth || 1
    newHeight = el.offsetHeight || 1
    if (newWidth !== lastWidth || newHeight !== lastHeight) {
        requestAnimationFrame(onResize)
    }
    expand.scrollTop = shrink.scrollTop = maxHeight
    expand.scrollLeft = shrink.scrollLeft = maxWidth
  }

  expand.addEventListener('scroll', onScroll, false)
  shrink.addEventListener('scroll', onScroll, false)
}

// 获取样式
function getStyle (obj, name) {
  if(window.getComputedStyle) {
    return getComputedStyle(obj, null)[name];
  } else {
    return obj.currentStyle[name];
  }
}

export default {
  data () {
    return {
      // 弹窗显示参数
      popoverVisible: false,
      // 当前选中的key
      checkedKeys: JSON.parse(JSON.stringify(this.value)),
      // 默认展开的key
      expandedKeys: [],

      // 延时器
      timer: null,

      // 弹窗默认宽度
      popperWidth: 200,
      popperWidthTimer: null,

    }
  },
  model: {
    prop: 'value',
    event: 'value-change'
  },
  props: {
    // v-model绑定的值
    value: {
      default: ''
    },

    // 数据列表
    options: {
      default () {
        return []
      }
    },

    // 选项属性名配置参数
    valueKey: {
      default: 'value'
    },
    labelKey: {
      default: 'label'
    },
    childrenKey: {
      default: 'children'
    },

    // 尺寸
    size: {
      default: ''
    },

    // 是否禁用
    disabled: {
      default: false
    },

    // 是否可以多选
    multiple: {
      default: false
    },

    // 是否可清空
    clearable: {
      default: true
    },

    // 是否可搜索
    filterable: {
      default: false
    },

    // 是否折叠tags
    collapseTags: {
      default: false
    },

    // 是否只显示所有等级
    showAllLevels: {
      default: false
    },
    // 分隔符
    separator: {
      default: '/'
    },

  },
  computed: {
    // 下拉框配置
    cascaderProps () {
      return {
        value: this.valueKey,
        label: this.labelKey,
        children: this.childrenKey,
        checkStrictly: true, // 是否可以选择任意一级
        multiple: this.multiple, // 是否可以多选
      }
    },

    // 树配置
    treeProps () {
      return {
        label: this.labelKey,
        children: this.childrenKey,
      }
    },

    // 下拉框值
    cascaderVal: {
      get () {
        // 多选
        if (this.multiple) {
          if (!this.value || !this.value.length) {
            return [];
          }
          var aValue = [];
          for (var i = 0; i < this.value.length; i++) {
            aValue.push(this.getTreeArrayValue(this.options, this.value[i], this.valueKey, this.childrenKey));
          }
          return aValue;
        // 单选
        } else {
          return this.getTreeArrayValue(this.options, this.value, this.valueKey, this.childrenKey);
        }
      },
      set (arr) {
        var res = '';
        // 多选
        if (this.multiple) {
          res = [];
          for (var i = 0; i < arr.length; i++) {
            var item = arr[i];
            res.push(item[item.length - 1]);
          }
        // 单选
        } else {
          res = arr[arr.length - 1];
        }
        this.$emit('value-change', res);
      }
    },

  },
  watch: {
    value (newVal) {
      // 多选
      if (this.multiple) {
        this.$refs.tree.setCheckedKeys(newVal);
      // 单选
      } else {
        this.$refs.tree.setCurrentKey(newVal || null);
      }
    }
  },
  mounted () {
    // 设置弹窗宽度
    this.setPopperWidth();
  },
  methods: {
    // ------------------- 下拉框 ------------
    // 显示隐藏下拉框改变
    visibleChange (visible) {
      setTimeout(() => {
        if (visible && !this.popoverVisible) {
          this.$refs.toggleBtn.click();
        }
      }, 20);
    },

    // 输入改变时，过滤树，去抖延迟300毫秒
    keyup (event) {
      var keyword = event.target.value;
      clearTimeout(this.timer);
      this.timer = setTimeout(() => {
        // 树过滤
        this.$refs.tree.filter(keyword);
      }, 300);
    },

    // -------------------- 树 ----------------
    // 根据选中项设置默认状态
    setDefaultState () {
      // 设置默认展开
      var aKeys = JSON.parse(JSON.stringify(this.cascaderVal));
      // 多选
      if (this.multiple) {
        var arr =[];
        for (var i = 0; i < aKeys.length; i++) {
          for (var j = 0; j < aKeys[i].length - 1; j++) {
            arr.push(aKeys[i][j]);
          }
        }
        // 去重
        aKeys = [];
        for (var i = 0; i < arr.length; i++) {
          var val = arr[i];
          if (aKeys.indexOf(val) == -1) {
            aKeys.push(val);
          }
        }
      // 单选
      } else {
        if (aKeys.length > 1) {
          aKeys.pop();
        } else {
          aKeys = [];
        }
      }
      this.expandedKeys = aKeys;

      // 设置单选时默认选中值
      if (!this.multiple) {
        this.$refs.tree.setCurrentKey(this.value);
      }

      // 清空搜索
      if (this.filterable) {
        this.$refs.tree.filter('');
      }
    },

    // 当前选中结点改变 - 只有单选时（current选中改变时）触发
    currentChange (current, node) {
      this.$emit('value-change', current[this.valueKey]);
      this.$refs.toggleBtn.click();
    },

    // 多选，选项改变时触发
    checkChange () {
      this.$emit('value-change', this.$refs.tree.getCheckedKeys());
    },

    // 过滤
    filterNodeMethod (value, data) {
      if (!value) return true;
      return data[this.labelKey].indexOf(value) !== -1;
    },

    // -------------------- 方法 ---------------
    /**
     *获取数组值根据选中的当前值
     *@param {Array} list [树形数组]
     *@param {Number,String} current [当前选中的值]
     *@param {String} valueKey [值的属性名]
     *@param {String} childrenKey [子节点数组的属性名]
     *@return {Array} [选中的数组值]
     */
    getTreeArrayValue (list, current, valueKey, childrenKey) {
      // 设置默认参数
      valueKey = valueKey || 'value';
      childrenKey = childrenKey || 'children';

      var res = '';
      if (!isEmpty(current)) {
        ergodic(list, []);
      }
      return res;

      // 判断空
      function isEmpty (val) {
        if (typeof val == 'string') {
          val = val.trim();
        }
        return !val && val!==0 && typeof val!=="boolean"?true:false;
      }

      // 遍历
      function ergodic (arr, pValue) {
        for (var i = 0; i < arr.length; i++) {
          var item = arr[i];
          // 判断是否已经获取到了值，退出循环
          if (res) {
            break;
          }

          // 拷贝数组，防止所有值跑到一个数组中了
          var aValue = JSON.parse(JSON.stringify(pValue));
          
          var val = item[valueKey]; // 当前值
          var aChildren = item[childrenKey]; // 子节点数组
          // 添加当前值到数组中
          aValue.push(val);
          // 如果找到当前值，退出循环
          if (val == current) {
            res = aValue;
            break;
          // 如果没找到值，并且有子节点，继续遍历
          } else if (aChildren && aChildren.length) {
            ergodic(aChildren, aValue);
          }
        }
      }
    },

    /**
     *设置并监听弹窗宽度
     */
    setPopperWidth () {
      var _this = this;
      var dom = this.$refs.toggleBtn;

      // 执行一次
      setWidth();
      // 监听宽度变化
      onelresize(dom, setWidth);

      function setWidth () {
        clearTimeout(_this.popperWidthTimer);
        _this.popperWidthTimer = setTimeout(() => {
          var width = parseInt(getStyle(dom, 'width'));
          if (!(width >= 200)) {
            width = 200;
          }
          _this.popperWidth = width;
        }, 30);
      }
    },

  }
}
</script>
<style lang="scss" scoped>
.my-select-tree-wp {
  display: inline-block;
  width: 100%;
  & > span {
    display: none;
  }
}
</style>
<style lang="scss">

.the-cascader {
  width: 100%;
  .el-input .el-icon-arrow-down.is-reverse {
    transform: rotateZ(0);
  }
}

.my-select-tree-hide-popper {
  display: none !important;
}

.my-select-tree-popper {
  padding: 0;

  // 将箭头位置固定
  .popper__arrow {
    left: 35px !important;
  }

  .scroll-box {
    padding: 6px 0;
    .el-scrollbar__wrap {
      margin-bottom: 0 !important;
      min-height: 150px;
      max-height: 274px;
      overflow-x: hidden;
    }

    .tree {
      // 设置高亮当前选中行颜色
      &.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content {
        background: #BAE7FF;
      }
    }
  }
}

</style>

